package com.gw.booking.service;

import com.alibaba.fastjson.JSONObject;
import com.gw.booking.common.*;
import com.gw.booking.dto.PageDto;
import com.gw.booking.dto.backend.*;
import com.gw.booking.dto.common.GPSCalculateDto;
import com.gw.booking.dto.wechat.*;
import com.gw.booking.entity.*;
import com.gw.booking.util.QRCodeUtils;
import config.Config;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class MerchantService {
    @Autowired
    private SessionFactory sessionFactory;
    @Autowired
    private RandomNumberService randomNumberService;
    @Autowired
    private LoginTokenService loginTokenService;
    @Autowired
    private LogService logService;
    @Autowired
    private AttachmentService attachmentService;
    @Autowired
    private AccessTokenService accessTokenService;
    @Autowired
    private CityService cityService;
    @Autowired
    private MerchantTypeService merchantTypeService;

    public PageDto<MerchantListDtoOut> list(String shopName, String manager, Integer merchantTypeId
            , int page, int size, LoginToken loginToken) {
        Session session = sessionFactory.getCurrentSession();
        Map<String, Object> params = new HashMap<>();
        params.put("cityId", loginToken.getCityId());
        String sqlCount = " select count(*) from merchant m inner join city on city.id = m.city_id left join customer c on m.open_id = c.open_id ";
        String sql = " select m.id as id, m.shop_name as shopName, m.address as address, m.phone as phone " +
                " , m.shopkeeper as shopkeeper, m.mobile as mobile ,m.wechat as wechat, m.open_id as openId, m.manager as manager " +
                " , m.shop_img1 as shopImg1, m.shop_img2 as shopImg2, m.shop_img3 as shopImg3 " +
                " , m.city_id as cityId, city.name as cityName, city.name_c as cityNameC " +
                " , m.merchant_type_id as merchantTypeId, mt.type_name as merchantTypeName, m.state " +
                " from merchant m inner join city on city.id = m.city_id " +
                " left join customer c on m.open_id = c.open_id " +
                " inner join merchant_type mt on mt.id = m.merchant_type_id ";
        String sqlWhere = " where 1 = 1 and m.city_id = :cityId ";
        String sqlOrder = " order by m.id desc ";
        if (StringUtils.isNotBlank(shopName)) {
            params.put("shopName", "%" + shopName + "%");
            sqlWhere += " and m.shop_name like :shopName ";
        }
        if (StringUtils.isNotBlank(manager)) {
            params.put("manager", "%" + manager + "%");
            sqlWhere += " and m.manager like :manager ";
        }
        if (merchantTypeId != null) {
            params.put("merchantTypeId", merchantTypeId);
            sqlWhere += " and m.merchant_type_id = :merchantTypeId ";
        }
        BigInteger total = (BigInteger) session.createSQLQuery(sqlCount + sqlWhere)
                .setProperties(params)
                .uniqueResult();
        List<MerchantListDtoOut> items = session.createSQLQuery(sql + sqlWhere + sqlOrder)
                .setProperties(params)
                .setResultTransformer(Transformers.aliasToBean(MerchantListDtoOut.class))
                .setFirstResult(Util.calFirst(page, size))
                .setMaxResults(size)
                .list();
        return new PageDto<>(page, size, total.longValue(), items);
    }

    public MerchantDetailDtoOut detail(Integer merchantId, LoginToken loginToken) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        if (!merchant.getCityId().equals(loginToken.getCityId())) {
            throw new ApiException(MessageCode.CODE_NO_PRIVILEGE);
        }
        City city = cityService.get(merchant.getCityId());
        MerchantType merchantType = merchantTypeService.sureMerchantType(session, merchant.getMerchantTypeId());
        MerchantDetailDtoOut merchantDetailDtoOut = new MerchantDetailDtoOut();
        PropertyUtils.copyProperties(merchantDetailDtoOut, merchant);
        merchantDetailDtoOut.setCityName(city.getName());
        merchantDetailDtoOut.setCityNameC(city.getNameC());
        merchantDetailDtoOut.setMerchantTypeId(merchantType.getId());
        merchantDetailDtoOut.setMerchantTypeName(merchantType.getTypeName());
        return merchantDetailDtoOut;
    }

    public void txAdd(LoginToken loginToken, MerchantDto dto) throws Exception {
        Session session = sessionFactory.getCurrentSession();

        City city = cityService.get(loginToken.getCityId());
        merchantTypeService.sureMerchantType(session, dto.getMerchantTypeId());

        Merchant merchant = new Merchant();
        Date datetime = new Date();
        merchant.setCreatedAt(new Timestamp(datetime.getTime()));
        merchant.setCityId(city.getId());
        merchant.setState(Constant.TRUE);
        PropertyUtils.copyProperties(merchant, dto);
        String shopImg1 = attachmentService.moveTmpFile(session, dto.getShopImg1(), Config.merchantFolder);
        String shopImg2 = attachmentService.moveTmpFile(session, dto.getShopImg2(), Config.merchantFolder);
        String shopImg3 = attachmentService.moveTmpFile(session, dto.getShopImg3(), Config.merchantFolder);
        merchant.setShopImg1(shopImg1);
        merchant.setShopImg2(shopImg2);
        merchant.setShopImg3(shopImg3);
        session.save(merchant);

        //生成二维码并保存图片
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String bindMerchantId = sdf.format(datetime) + "bind_merchant_id_" + merchant.getId() + ".png";
        String detailMerchantId = sdf.format(datetime) + "detail_merchant_id_" + merchant.getId() + ".png";
        String qrBindImgUrl = QRCodeUtils.buildMerchantQRCode(Config.qrCodeBindFolder + bindMerchantId, Constant.BIND_MERCHANT_URL + merchant.getId(), false);
        String qrDetailImgUrl = QRCodeUtils.buildMerchantQRCode(Config.qrCodeDetailFolder + detailMerchantId, Constant.FIND_MERCHANT_DETAIL_URL + merchant.getId(), true);
        merchant.setQrBindImgUrl(qrBindImgUrl);
        merchant.setQrDetailImgUrl(qrDetailImgUrl);
        session.update(merchant);

        //附件相关信息
        Attachment attachment = new Attachment();
        attachment.setFilename(qrBindImgUrl);
        attachment.setFilenameOriginal(bindMerchantId);
        attachment.setMime("image/png");
        attachment.setCreatedAt(new Timestamp(datetime.getTime()));
        session.save(attachment);
        Attachment attachment1 = new Attachment();
        attachment1.setFilename(qrDetailImgUrl);
        attachment1.setFilenameOriginal(detailMerchantId);
        attachment1.setMime("image/png");
        attachment1.setCreatedAt(new Timestamp(datetime.getTime()));
        session.save(attachment1);
        session.flush();
        logService.txAddLog(Logs.LogType.add, loginToken.getUserId(), "新增商家：" + merchant.getId());
    }

    public void txUpdate(LoginToken loginToken, MerchatDetailDto merchatDetailDto) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException, IOException {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchatDetailDto.getMerchartId());
        if (!merchant.getCityId().equals(loginToken.getCityId())) {
            throw new ApiException(MessageCode.CODE_NO_PRIVILEGE);
        }
        merchantTypeService.sureMerchantType(session, merchatDetailDto.getMerchantTypeId());

        merchant.setUpdatedAt(new Timestamp(System.currentTimeMillis()));
        PropertyUtils.copyProperties(merchant, merchatDetailDto);
        String shopImg1 = attachmentService.moveTmpFile(session, merchatDetailDto.getShopImg1(), Config.merchantFolder);
        String shopImg2 = attachmentService.moveTmpFile(session, merchatDetailDto.getShopImg2(), Config.merchantFolder);
        String shopImg3 = attachmentService.moveTmpFile(session, merchatDetailDto.getShopImg3(), Config.merchantFolder);
        merchant.setShopImg1(shopImg1);
        merchant.setShopImg2(shopImg2);
        merchant.setShopImg3(shopImg3);
        session.save(merchant);
        session.flush();
        logService.txAddLog(Logs.LogType.edit, loginToken.getUserId(), "编辑商家：" + merchant.getId());
    }

    public void txDelete(LoginToken loginToken, Integer merchantId) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        if (merchant == null) {
            throw new ApiException(MessageCode.CODE_MERCHANT_NOT_EXISTS);
        }
        session.delete(merchant);
        session.flush();
        logService.txAddLog(Logs.LogType.delete, loginToken.getUserId(), "删除商家：" + merchant.getId());
    }

    public void txUpdateState(LoginToken loginToken, Integer merchantId, Integer state) {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        if (merchant == null) {
            throw new ApiException(MessageCode.CODE_MERCHANT_NOT_EXISTS);
        }
        merchant.setState(state);
        session.flush();
        logService.txAddLog(Logs.LogType.edit, loginToken.getUserId(), "修改商家状态：" + merchant.getId()
                + (state.equals(Constant.TRUE) ? "公开" : "非公开"));
    }

    public void txUnbind(LoginToken loginToken, Integer merchantId) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        if (merchant == null) {
            throw new ApiException(MessageCode.CODE_MERCHANT_NOT_EXISTS);
        }
        if (merchant.getOpenId() == null) {
            throw new ApiException(MessageCode.CODE_NO_BINDED);
        }
        merchant.setOpenId(null);
        merchant.setWechat(null);
        merchant.setHeadImgUrl(null);
        session.update(merchant);
        session.flush();
        logService.txAddLog(Logs.LogType.edit, loginToken.getUserId(), "商家解绑微信：" + merchant.getId());
    }

//    public Merchant getMerchant(Integer merchantId) {
//        Session session = sessionFactory.getCurrentSession();
//        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
//        return merchant;
//    }

    public PageDto<WMerchantListDtoOut> listForDiner(LoginToken loginToken, String cityName, String shopName, Integer merchantTypeId
            , String longitude, String latitude, int page, int size) {
        Session session = sessionFactory.getCurrentSession();
        Map<String, Object> params = new HashMap<>();
        Double lon;
        Double lat;
        City city = cityService.getCityByName(cityName);
        if (StringUtils.isNotBlank(longitude) && StringUtils.isNotBlank(latitude)) {
            lon = Double.parseDouble(longitude);
            lat = Double.parseDouble(latitude);
        } else {
            lon = city.getLongitude();
            lat = city.getLatitude();
        }
        GPSCalculateDto gpsCalculateDto = Util.gpsCalculate(lon, lat, Constant.RANGE_LONG);
        params.put("minlat", gpsCalculateDto.getMinlat());
        params.put("maxlat", gpsCalculateDto.getMaxlat());
        params.put("minlng", gpsCalculateDto.getMinlng());
        params.put("maxlng", gpsCalculateDto.getMaxlng());
        params.put("cityId", city.getId());
        String hqlCount = " select count(*) from merchant m inner join merchant_type mt on mt.id = m.merchant_type_id ";
        String hql = "  select " +
                "       m.id as merchantId, " +
                "       m.shop_name as shopName, " +
                "       m.shop_img1 as shopImg1, " +
                "       m.shop_img2 as shopImg2, " +
                "       m.shop_img3 as shopImg3, " +
                "       m.content as content, " +
                "       count(d.id) as discountNum, " +
                "       m.latitude as latitude, " +
                "       m.longitude as longitude," +
                "       m.merchant_type_id as merchantTypeId," +
                "       mt.type_name as merchantTypeName " +
                "   from merchant m left join discount d on d.merchant_id = m.id and d.state = 1 " +
                "   inner join merchant_type mt on mt.id = m.merchant_type_id ";
        String hqlWhere = " where 1 = 1 and m.state = 1 and m.longitude >= :minlng and m.longitude <= :maxlng " +
                " and m.latitude >= :minlat and m.latitude <= :maxlat and m.city_id = :cityId";
        if (StringUtils.isNotBlank(shopName)) {
            params.put("shopName", "%" + shopName + "%");
            hqlWhere += " and (m.shop_name like :shopName or m.content like :shopName )";
        }
        if (merchantTypeId != null) {
            params.put("merchantTypeId", merchantTypeId);
            hqlWhere += " and m.merchant_type_id = :merchantTypeId ";
        }
        String hqlOrder = "";
        String sqlGroup = " group by m.id,m.shop_name,m.shop_img1, m.shop_img2,m.shop_img3,m.address, m.latitude,m.longitude";
        if (StringUtils.isNotEmpty(latitude) && StringUtils.isNotEmpty(longitude)) {
            hqlOrder = "    order by sqrt((m.latitude - :latitude) * (m.latitude - :latitude)" +
                    "   + (m.longitude - :longitude) * (m.longitude - :longitude))";
            params.put("latitude", Double.valueOf(latitude));
            params.put("longitude", Double.valueOf(longitude));
        }
        BigInteger total = (BigInteger) session.createSQLQuery(hqlCount + hqlWhere).setProperties(params).uniqueResult();
        List<WMerchantListDtoOut> items = session.createSQLQuery(hql + hqlWhere + sqlGroup + hqlOrder).setProperties(params)
                .setResultTransformer(Transformers.aliasToBean(WMerchantListDtoOut.class))
                .setFirstResult(Util.calFirst(page, size))
                .setMaxResults(size)
                .list();
        for (WMerchantListDtoOut wMerchantListDtoOut : items) {
            wMerchantListDtoOut.setDistance(Util.getDistance(wMerchantListDtoOut.getLongitude()
                    , wMerchantListDtoOut.getLatitude(), lon, lat));
        }
        return new PageDto<>(page, size, total.longValue(), items);

    }

    public WMerchantDetailDtoOut detailForDiner(LoginToken loginToken, Integer merchantId, String longitude, String latitude) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        if (merchant == null || Constant.FALSE.equals(merchant.getState())) {
            throw new ApiException(MessageCode.CODE_MERCHANT_NOT_EXISTS);
        }
        MerchantType merchantType = merchantTypeService.sureMerchantType(session, merchant.getMerchantTypeId());
        WMerchantDetailDtoOut wMerchantDetailDtoOut = new WMerchantDetailDtoOut();
        PropertyUtils.copyProperties(wMerchantDetailDtoOut, merchant);
        wMerchantDetailDtoOut.setMerchantTypeName(merchantType.getTypeName());
        wMerchantDetailDtoOut.setOrderTimeNum(merchantType.getOrderTimeNum());

        List<Discount> discountList = session.createQuery(" from Discount where merchantId = :merchantId and state = 1 ")
                .setParameter("merchantId", merchantId).list();
        List<WDIscountDtoOut> items = new ArrayList<>();
        List<CustomerDiscount> customerDiscountList = session.createQuery("from CustomerDiscount where customerId = :customerId ")
                .setParameter("customerId", loginToken.getUserId()).list();
        for (Discount discount : discountList) {
            WDIscountDtoOut wdIscountDtoOut = new WDIscountDtoOut();
            PropertyUtils.copyProperties(wdIscountDtoOut, discount);
            for (CustomerDiscount customerDiscount : customerDiscountList) {
                if (customerDiscount.getDiscountId().equals(discount.getId())) {
                    wdIscountDtoOut.setCustomerDiscountId(customerDiscount.getId());
                }
            }
            items.add(wdIscountDtoOut);
        }
        wMerchantDetailDtoOut.setWdIscountDtoOutList(items);
        wMerchantDetailDtoOut.setDistance(Util.getDistance(wMerchantDetailDtoOut.getLongitude()
                , wMerchantDetailDtoOut.getLatitude(), Double.parseDouble(longitude), Double.parseDouble(latitude)));
        return wMerchantDetailDtoOut;
    }

    /**
     * 微信登陆（商家端）
     *
     * @param code
     * @param ip
     * @return
     */
    public WLoginDtoOut txLoginMerchant(String code, String ip) {
        Session session = sessionFactory.getCurrentSession();
        JSONObject jsonObject = WXAppletUserInfo.getSessionKeyOropenidForMerchant(code);
        String openId = jsonObject.get("openid") == null ? null : jsonObject.get("openid").toString();
        if (openId == null) {
            throw new ApiException(MessageCode.CODE_WXCODE_ERROR);
        }
        Merchant merchant = (Merchant) session.createQuery("from Merchant where openId = :openId")
                .setParameter("openId", openId).uniqueResult();
        if (merchant == null) {
            throw new ApiException(MessageCode.CODE_NO_BINDED);
        }
        LoginToken loginToken = loginTokenService.txAddLoginToken(session, Constant.USER_TYPE_MERCHANT, merchant.getId(), ip, null);
        WLoginDtoOut wLoginDtoOut = new WLoginDtoOut();
        wLoginDtoOut.setCustomerId(merchant.getId());
        wLoginDtoOut.setToken(loginToken.getToken());
        wLoginDtoOut.setType(Constant.USER_TYPE_MERCHANT);
        return wLoginDtoOut;
    }

    /**
     * 绑定商家微信
     *
     * @param wMerchantDto
     * @param ip
     * @return
     */
    public WLoginDtoOut txAddMerchant(WMerchantDto wMerchantDto, String ip) {
        Session session = sessionFactory.getCurrentSession();
        JSONObject jsonObject = WXAppletUserInfo.getSessionKeyOropenidForMerchant(wMerchantDto.getCode());
        String openId = jsonObject.get("openid") == null ? null : jsonObject.get("openid").toString();
        if (openId == null) {
            throw new ApiException(MessageCode.CODE_WXCODE_ERROR);
        }
        //检查微信是否被绑定商家
        List<Merchant> merchantCheck = session.createQuery("from Merchant where openId = :openId")
                .setParameter("openId", openId).list();
        if (merchantCheck.size() > 0) {
            throw new ApiException(MessageCode.CODE_UNIONID_BINDED);
        }
        //检查商家存在性，是否被绑定
        Merchant merchant = (Merchant) session.get(Merchant.class, wMerchantDto.getMerchantId());
        if (merchant == null) {
            throw new ApiException(MessageCode.CODE_MERCHANT_NOT_EXISTS);
        }
        if (merchant.getOpenId() != null) {
            throw new ApiException(MessageCode.CODE_MERCHANT_UNIONID_BINDED);
        }
        //检查用户是否关注公众号
        String accessToken = accessTokenService.txGetAccessToken(Constant.USER_TYPE_MERCHANT);
        JSONObject jsonObject1 = WXAppletUserInfo.getIdentificationForMerchant(accessToken, openId);
        if (jsonObject1.get("nickname") != null) {
            String nickName = Util.string2Unicode(jsonObject1.get("nickname").toString());
            merchant.setWechat(nickName);
        }
        if (jsonObject1.get("headimgurl") != null) {
            merchant.setHeadImgUrl(jsonObject1.get("headimgurl").toString());
        }
        merchant.setOpenId(openId);
        session.update(merchant);
        LoginToken loginToken = loginTokenService.txAddLoginToken(session, Constant.USER_TYPE_MERCHANT, merchant.getId(), ip, null);
        WLoginDtoOut wLoginDtoOut = new WLoginDtoOut();
        wLoginDtoOut.setCustomerId(merchant.getId());
        wLoginDtoOut.setToken(loginToken.getToken());
        wLoginDtoOut.setType(Constant.USER_TYPE_MERCHANT);
        return wLoginDtoOut;
    }

    public void txBuildMerchantImg(Integer merchantId) throws Exception {
        Session session = sessionFactory.getCurrentSession();
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String qrBindImgUrl = QRCodeUtils.buildMerchantQRCode(Config.qrCodeBindFolder + sdf.format(new Date()) + "bind_merchant_id_" + merchant.getId() + ".png", Constant.BIND_MERCHANT_URL + merchant.getId(), false);
        String qrDetailImgUrl = QRCodeUtils.buildMerchantQRCode(Config.qrCodeDetailFolder + sdf.format(new Date()) + "detail_merchant_id_" + merchant.getId() + ".png", Constant.FIND_MERCHANT_DETAIL_URL + merchant.getId(), true);
        merchant.setQrBindImgUrl(qrBindImgUrl);
        merchant.setQrDetailImgUrl(qrDetailImgUrl);
        session.update(merchant);
        session.flush();
    }

    public Merchant sureMerchant(Session session, Integer merchantId) {
        Merchant merchant = (Merchant) session.get(Merchant.class, merchantId);
        if (merchant == null) {
            throw new ApiException(MessageCode.CODE_MERCHANT_NOT_EXISTS);
        }
        return merchant;
    }

    public void checkMerchantType(Session session, Integer merchantTypeId) {
        String sql = " from Merchant where merchantTypeId = :merchantTypeId ";
        List<Merchant> merchantList = session.createQuery(sql).setParameter("merchantTypeId", merchantTypeId).list();
        if (merchantList != null && merchantList.size() > 0) {
            throw new ApiException(MessageCode.CODE_MERCHANT_TYPE_USED);
        }
    }

}
